package com.feixiang.springbootinit.wechat;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.feixiang.springbootinit.common.Result;
import com.feixiang.springbootinit.utils.RSAUtil;
import com.tencent.wework.Finance;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.FileOutputStream;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
/**
 * 企微会话存档加密解密调用的方法
 */
public class SessionArchive {

    //引入本地缓存(caffeine)
    private static Map<String, Long> sdkMap = new HashMap<>();

    /**
     * 第一步:初始化sdk,获取sdk对象，首次使用初始化，第二步:初始化函数,return 返回是否初始化成功 0:-成功,!=0:-失败
     *
     * @param sdkId      sdkid
     * @param corpid     应用id
     * @param secrectkey 密钥
     * @return
     */
    public Result initSdk(String sdkId, String corpid, String secrectkey) {
        long ret;
        Long sdk = Finance.NewSdk();
        ret = Finance.Init(sdk, corpid, secrectkey);
        if (ret != 0) {
            Finance.DestroySdk(sdk);
            sdkMap.remove(sdkId);
            return new Result(ret, "init sdk err", null);
        }
        if (StrUtil.isNotBlank(sdkId)) {
            sdkMap.put(sdkId, sdk);
            return Result.success("init sdk success", sdkId);
        } else {
            sdkMap.put(String.valueOf(sdk), sdk);
            return Result.success("init sdk success", String.valueOf(sdk));
        }
    }

    /**
     * 第二步拉取会话数据，这里是没有解密的。
     *
     * @param sdkId   sdkId
     * @param seq     会话seq
     * @param limit   会话条数
     * @param proxy   代理名     没有则为空
     * @param passwd  代理密码    没有则为空
     * @param timeout 超时时间
     * @return
     */
    public Result getChatData(String sdkId, int seq, int limit, String proxy, String passwd, int timeout) {
        Long sdk = sdkMap.get(sdkId);
        if (ObjectUtil.isNull(sdk)) {
            return Result.error("init sdk err");
        }
        log.info("获取GetChatData参数：sdk:{},seq:{},limit:{},proxy:{}", sdk, seq, limit, proxy);
        //每次使用GetChatData拉取存档前需要调用NewSlice获取一个slice，在使用完slice中数据后，还需要调用FreeSlice释放。
        long slice = Finance.NewSlice();
        long ret = Finance.GetChatData(sdk, seq, limit, proxy, passwd, timeout, slice);
        if (ret != 0) {
            log.error("获取GetChatData失败:{}", ret);
            Finance.FreeSlice(slice);
            return new Result(ret, "获取GetChatData失败", null);
        }
        String result = Finance.GetContentFromSlice(slice);
        log.info("获取GetChatData成功:{}", result);
        Finance.FreeSlice(slice);
        return Result.success("获取GetChatData成功", result);
    }

    /**
     * 第三步：解密会话数据
     * 解密会话存档内容
     *
     * @param sdkId
     * @param pkv                                                                                                        私有-加密此条消息使用的公钥版本号。Uint32类型
     * @param encryptRandomKey                                                                                           使用publickey_ver指定版本的公钥进行非对称加密后base64加密的内容，需要业务方先base64 decode处理后，再使用指定版本的私钥进行解密，得出内容。String类型
     * @param encryptChatMsg-消息密文。需要业务方使用将encrypt_random_key解密得到的内容，与encrypt_chat_msg，传入sdk接口DecryptData,得到消息明文。String类型
     * @return
     */
    public Result decryptData(String sdkId, String pkv, String encryptRandomKey, String encryptChatMsg) {
        Long sdk = sdkMap.get(sdkId);
        if (ObjectUtil.isNull(sdk)) {
            return Result.error("init sdk err");
        }
        String encryptKey = RSAUtil.decrypt(encryptRandomKey, pkv);
        return decryptData(sdkId, encryptKey, encryptChatMsg);
    }


    /**
     * 解密会话存档内容
     *
     * @param sdkId
     * @param encryptKey
     * @param encryptChatMsg
     * @return
     */
    public Result decryptData(String sdkId, String encryptKey, String encryptChatMsg) {
        Long sdk = sdkMap.get(sdkId);
        if (ObjectUtil.isNull(sdk)) {
            return Result.error("init sdk err");
        }
        long msg = Finance.NewSlice();
        long ret = Finance.DecryptData(sdk, encryptKey, encryptChatMsg, msg);
        if (ret != 0) {
            System.out.println("decryptData ret " + ret);
            Finance.FreeSlice(msg);
            return new Result(ret, "decryptData err", null);
        }
        String result = Finance.GetContentFromSlice(msg);
        System.out.println("decryptData ret:" + ret + " msg:" + result);
        Finance.FreeSlice(msg);
        return Result.success("decryptData success", result);
    }


    /**
     * 第四步获取媒体文件
     * 拉取媒体文件
     *
     * @return
     */
    public Object getMediaDataAndUpload(String sdkId, String msgtype, JSONObject jsonObject, String proxy, String passwd, int timeout) {
        Map<String, Object> map = new HashMap<>();
        String basePath = "/home/jch/conversation/";
        Long sdk = sdkMap.get(sdkId);
        if (ObjectUtil.isNull(sdk)) {
            throw new RuntimeException("init sdk err");
        }
        try {
            String savefileName = "";
            JSONObject file = new JSONObject();
            if (!jsonObject.isNull("msgid")) {
                file = jsonObject.getJSONObject(msgtype);
                savefileName = jsonObject.getStr("msgid");
            } else {
                // 混合消息
                file = jsonObject;
                savefileName = file.getStr("md5sum");
            }
            log.info("媒体文件消息：{}", file.toString());
            /* ============ 文件存储目录及文件名 Start ============ */
            String suffix = "";
            switch (msgtype) {
                case "image":
                    suffix = ".jpg";
                    break;
                case "voice":
                    suffix = ".amr";
                    break;
                case "video":
                    suffix = ".mp4";
                    break;
                case "emotion":
                    int type = (int) file.get("type");
                    if (type == 1) {
                        suffix = ".gif";
                    } else if (type == 2) {
                        suffix = ".png";
                    }
                    break;
                case "file":
                    suffix = "." + file.getStr("fileext");
                    break;
            }
            savefileName += suffix;
            String path = basePath;
            String savefile = path + savefileName;
            File targetFile = new File(savefile);
            if (!targetFile.getParentFile().exists()) {
                //创建父级文件路径
                targetFile.getParentFile().mkdirs();
            }
            /* ============ 文件存储目录及文件名 End ============ */

            /* ============ 拉去文件 Start ============ */
            int i = 0;
            boolean isSave = true;
            String indexbuf = "";
            String sdkfileid = file.getStr("sdkfileid");
            while (true) {
                long mediaData = Finance.NewMediaData();
                int ret = Finance.GetMediaData(sdk, indexbuf, sdkfileid, proxy, passwd, timeout, mediaData);
                if (ret != 0) {
                    log.info("getmediadata ret:{}", ret);
                    Finance.FreeMediaData(mediaData);
                    return null;
                }
                log.info("getmediadata outindex len:{}, data_len:{}, is_finis:{}", Finance.GetIndexLen(mediaData), Finance.GetDataLen(mediaData), Finance.IsMediaDataFinish(mediaData));
                try {
                    // 大于512k的文件会分片拉取，此处需要使用追加写，避免后面的分片覆盖之前的数据。
                    FileOutputStream outputStream = new FileOutputStream(new File(savefile), true);
                    outputStream.write(Finance.GetData(mediaData));
                    outputStream.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (Finance.IsMediaDataFinish(mediaData) == 1) {
                    // 已经拉取完成最后一个分片
                    Finance.FreeMediaData(mediaData);
                    break;
                } else {
                    // 获取下次拉取需要使用的indexbuf
                    indexbuf = Finance.GetOutIndexBuf(mediaData);
                    Finance.FreeMediaData(mediaData);
                }
                // 若文件大于50M则不保存
                if (++i > 100) {
                    isSave = false;
                    break;
                }
            }
            /* ============ 拉去文件 End ============ */
            if (isSave) {
                file.set("sdkfileid", savefile);
                File file1 = new File(savefile);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //todo 保存到什么地方
        return "ok";
    }

    /**
     * 第五步：释放sdk，当本次拉取结束时，可以选择调用释放sdk
     * 释放sdk
     *
     * @param sdkId sdkid
     * @return
     */
    public Result destroySdk(String sdkId) {
        Long sdk = sdkMap.get(sdkId);
        if (ObjectUtil.isNull(sdk)) {
            return Result.error("init sdk err");
        }
        Finance.DestroySdk(sdk);
        sdkMap.remove(sdkId);
        return Result.success("destroySdk success");
    }
}
